package cn.ltgodm.moj.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.ltgodm.moj.codesandbox.CodeSandbox;
import cn.ltgodm.moj.codesandbox.CodeSandboxFactory;
import cn.ltgodm.moj.codesandbox.CodesandboxProxy;
import cn.ltgodm.moj.codesandbox.model.ExecuteCodeRequest;
import cn.ltgodm.moj.codesandbox.model.ExecuteCodeResponse;
import cn.ltgodm.moj.common.ErrorCode;
import cn.ltgodm.moj.exception.BusinessException;
import cn.ltgodm.moj.model.dto.question.JudgeCase;
import cn.ltgodm.moj.model.dto.question.JudgeCaseDTO;
import cn.ltgodm.moj.model.entity.Question;
import cn.ltgodm.moj.model.entity.QuestionSubmit;
import cn.ltgodm.moj.model.enums.QuestionJudgeInfomessageEnum;
import cn.ltgodm.moj.model.enums.QuestionSubmitStatusEnum;
import cn.ltgodm.moj.model.sandbox.JudgeInfo;
import cn.ltgodm.moj.model.vo.QuestionSubmitVO;
import cn.ltgodm.moj.service.JudgeFeignClient;
import cn.ltgodm.moj.service.JudgeService;
import cn.ltgodm.moj.service.QuestionFeignClient;
import cn.ltgodm.moj.strategy.JudgeContext;
import cn.ltgodm.moj.strategy.JudgeStrategyManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class JudgeServiceImpl implements JudgeService {
    @Resource
    private QuestionFeignClient questionFeignClient;


    @Resource
    private JudgeStrategyManager judgeStrategyManager;

    @Value("${codesandbox.type:example}")
    private String codesandboxType;

    @Override
    public QuestionSubmitVO doJudge(Long questionSubmitId) {
        //查询提交信息
        QuestionSubmit questionSubmit = getQuestionSubmit(questionSubmitId);

        //校验题目提交信息
        boolean isWaiting = verifyQuestionSubmitStatus(questionSubmit, QuestionSubmitStatusEnum.WAITING.getValue());
        if (!isWaiting) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目正在判题中");
        }

        //更新题目提交状态
        questionSubmit.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());
        boolean update = questionFeignClient.updateQuestionSubmitById(questionSubmit);
        if (!update) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新失败");
        }

        //获取题目信息
        Long questionId = questionSubmit.getQuestionId();
        Question question = getQuestion(questionId);

        //获取代码执行请求
        ExecuteCodeRequest codeRequest = getCodeRequestByQuestion(question, questionSubmit);

        //调用代码沙箱
        CodeSandbox codeSandbox = CodeSandboxFactory.newInstance(codesandboxType);
        CodesandboxProxy codesandboxProxy = new CodesandboxProxy(codeSandbox);
        ExecuteCodeResponse executeCodeResponse = codesandboxProxy.executeCode(codeRequest);


        //根据代码沙箱的返回结果，修改提交信息的数据，并且返回结果
        JudgeInfo judgeInfo = executeCodeResponse.getJudgeInfo();
        if (executeCodeResponse.getStatus().compareTo(2)==0){
            //编译错误
            judgeInfo.setMessage(QuestionJudgeInfomessageEnum.COMPILE_ERROR.getValue());
        }
        JudgeContext judgeContext = new JudgeContext();
        judgeContext.setJudgeInfo(judgeInfo);
        judgeContext.setOutputCaseList(executeCodeResponse.getOutputList());
        judgeContext.setQuestion(question);
        judgeContext.setQuestionSubmit(questionSubmit);

        return executeFormResponse(judgeContext);
    }

    /**
     * 处理代码执行返回结果
     * judgeContext 判题上下文
     *
     * @return QuestionSubmitVO 返回题目提交信息
     */
    private QuestionSubmitVO executeFormResponse(JudgeContext judgeContext) {
        //判题
        JudgeInfo judgeInfo = judgeStrategyManager.doJudge(judgeContext);

        //修改题目提交状态
        QuestionSubmit questionSubmit = judgeContext.getQuestionSubmit();
        questionSubmit.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());
        questionSubmit.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));
        boolean update = questionFeignClient.updateQuestionSubmitById(questionSubmit);
        if (!update) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新题目提交状态失败");
        }
        //修改题目提交数
        IncreaseQuestionNum(questionSubmit.getQuestionId(), judgeInfo.getMessage());

        //返回结果
        return QuestionSubmitVO.objToVo(questionSubmit);
    }

    private void IncreaseQuestionNum(Long questionId, String status) {
        Question question = questionFeignClient.getQuestionById(questionId);
        if (ObjectUtil.isNull(question)) return;
        Integer acceptedNum = question.getAcceptedNum();
        Integer submitNum = question.getSubmitNum();
        if (QuestionJudgeInfomessageEnum.ACCEPTED.getValue().equals(status)) {
            acceptedNum++;
        }
        submitNum++;
        Question questionDB = new Question();
        questionDB.setId(questionId);
        questionDB.setAcceptedNum(acceptedNum);
        questionDB.setSubmitNum(submitNum);
        questionFeignClient.updateQuestionById(questionDB);
    }


    /**
     * 通过题目提交信息获取题目信息
     *
     * @param questionId 题目提交信息
     * @return 返回题目信息
     */
    private Question getQuestion(Long questionId) {
        Question question = questionFeignClient.getQuestionById(questionId);
        if (ObjectUtil.isNull(question)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "对应题目信息不存在");
        }
        return question;
    }

    /**
     * 获取题目提交信息
     *
     * @param questionSubmitId 题目提交id
     * @return 题目提交信息
     */
    private QuestionSubmit getQuestionSubmit(Long questionSubmitId) {
        //获取题目提交信息
        QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);
        if (ObjectUtil.isNull(questionSubmit)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目提交信息不存在");
        }
        return questionSubmit;
    }

    /**
     * 根据题目信息和题目提交信息获取获取执行请求信息
     *
     * @param question       题目信息
     * @param questionSubmit 题目请求信息
     * @return 代码执行提交信息
     */
    private ExecuteCodeRequest getCodeRequestByQuestion(Question question, QuestionSubmit questionSubmit) {
        //将判题用例分别转换为输出用例数组和输入用例数组
        JudgeCaseDTO judgeCaseDTO = getCaseListMapFormJudeCase(question.getJudgeCase());
        //构造ExecuteCodeRequest并返回
        return ExecuteCodeRequest.builder()
                .code(questionSubmit.getCode())
                .language(questionSubmit.getLanguage())
                .inputList(judgeCaseDTO.getInputList())
                .build();
    }

    /**
     * 将判题用例分别转换为输出用例数组和输入用例数组
     *
     * @param judgeCase 判题用例
     * @return 判题用例
     */
    private JudgeCaseDTO getCaseListMapFormJudeCase(String judgeCase) {
        if (StrUtil.isEmptyIfStr(judgeCase)) {
            return null;
        }
        List<JudgeCase> judgeCaseList = JSONUtil.toList(judgeCase, JudgeCase.class);
        List<String> inputList = judgeCaseList.stream().map(JudgeCase::getInput).collect(Collectors.toList());
        List<String> outputList = judgeCaseList.stream().map(JudgeCase::getOutput).collect(Collectors.toList());
        JudgeCaseDTO judgeCaseDTO = new JudgeCaseDTO();
        judgeCaseDTO.setInputList(inputList);
        judgeCaseDTO.setOutputList(outputList);
        return judgeCaseDTO;
    }

    /**
     * 校验题目提交状态
     *
     * @param questionSubmit 题目提交信息
     * @param statusValue    状态枚举值
     * @return 对比结果
     */
    private boolean verifyQuestionSubmitStatus(QuestionSubmit questionSubmit, Integer statusValue) {
        return ObjectUtil.isNotNull(questionSubmit) && questionSubmit.getStatus().compareTo(statusValue) == 0;
    }
}
